在上一章的課程介紹了裝飾器的基本語法與應用,還有介紹非特定參數的傳入方式以及@
語法糖的概念,接下來要介紹一些進階的內容。
本文章同步放置於此
在介紹完@
語法糖以及類別後就可以進一步介紹@staticmethod
與@classmethod
,其實看到@
讀者們就知道看到裝飾器了,至於這兩個裝飾器有何作用呢?
這是靜態函式的裝飾器,當有些類別的方法想要直接使用時會遇到一個窘境,還記得類別方法是宣告的嗎,看看下列式子吧:
class ObjectA:
def func(self, *args, **kwargs):
print('do some thing')
好了問題來了,大家還記得類別方法的第一個參數是甚麼嗎?那就是類別的實體本身,因此要使用此類別的方法實際就要依照下列式子的方式來使用:
var = ObjectA()
var.func()
不過讀者會問說,我僅是要使用該方法,為什麼要產生該物件的實體呢?這時候@staticmethod
就需要出馬了,最上面類別的宣告要改成以下方式:
class ObjectA:
@staticmethod
def func(*args, **kwargs):
print('do some thing')
如此要使用該靜態方法就不需要產生類別的實體了。
相較於靜態方方法,還有另外一種裝飾器就是類別方法@classmethod
,相對於靜態方法的裝飾器可以使類別方法不需要綁定在物件實體本身上,類別方法裝飾器則是將類別方法強制給予一個類別實體,請各位先看看以下宣告的方式:
class ObjectB:
@classmethod
def func(cls, *args, **kwargs):
print('do some thing')
雖然觀念不太一樣,但是類別方法的使用方式與靜態方法一樣,就是透過類別名稱.類別方法的方式直接使用,無須產生類別實體。
同時講了靜態方法與類別方法,讀者應該會搞混,這兩個裝飾器用下去該方法都不需要綁定物件的實體,那這兩個裝飾器差別在哪裡,甚麼時候要用哪一個呢?這時候把兩個宣告方式擺再一起看一看:
# 靜態方法
@staticmethod
def func(*args, **kwargs):
# 類別方法
@classmethod
def func(cls, *args, **kwargs):
其實答案就在方法的宣告上,靜態方法裝飾器上沒有給予類別的實體,而類別方法裝飾器第一個參數就是綁訂在一個類別物件的實體cls
,這時候不管產生多少類別物件的實體,他所用到的類別方法所綁定的類別物件實體都會是同一個,所以說需要用到一個共用的類別物件的實體時就需要使用類別方法。
裝飾器不僅可以以方法的方式來創建,亦可以以類別的方式來創建,請大家看看下列例子:
import types
class ClassDecorator:
def __init__(self, func):
self.numberOfCalls = 0
self.func = func
def __call__(self, *args, **kwargs):
self.numberOfCalls += 1
return self.func(*args, **kwargs)
def __get__(self, instance, cls):
if instance is None:
return self
else:
return types.MethodType(self, instance)
以上例子就是如何創建一個列別裝飾器,眼尖的讀者看到兩個新的關鍵字__call__
與__get__
,在解釋這兩個關鍵字之前先看看類別裝飾器如何使用:
@ClassDecorator
def add(x, y):
return x + y
class Decorated:
@ClassDecorator
def bar(self, x):
print(self, x)
print(add(2, 3))
print(add(4, 5))
print(add.numberOfCalls)
s = Decorated()
s.bar(1)
s.bar(2)
s.bar(3)
print(Decorated.bar.numberOfCalls)
由例子可以看到__call__
是定義當該方法被呼叫到時的處置方式,那許多讀者就會問說__get__
是做甚麼用的呢,他是有必要存在的嗎。這時讀者們可以自行把__get__
去除掉是是看,相信沒了這東西應該會看調下列錯誤訊息:
TypeError: bar() missing 1 required positional argument: 'x'
由此可知__get__
方法是为了確保綁定方法對象能被正確的創建。不過若是@ClassDecorator
僅使用於方法上則__get__
可有可無,不過若是要使用在類別的方法上,則__get__
就一定需要實作了。關於這部分的知識有興趣的讀者可以參考python3-cookbook->9.9 将装饰器定义为类。
在理解@
語法糖後相信下列式子應該不難理解:
@a
@b
@c
def d():
pass
不知道所有的讀者是否有答案了呢,如果沒有答案的話可以看看下列更進一步的解釋:
d = a(b(c(d)))
其實最前面的式子與上述式子式等價的,所以由此可知多裝幾層裝飾器它是由上而下的包裝起來的,所以執行時會由上而下的執行完裝飾器後再執行方法本身。
好了,裝飾器的進階內容說明完了,接下來要介紹如何使用別人寫的函式庫以及如何自己撰寫函式庫,敬請期待。